package com.nbst.service.api.impl;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.nbst.cache.OpenMethodCache;
import com.nbst.cache.OpenSystemCache;
import com.nbst.comnutil.HttpUtil;
import com.nbst.comnutil.MD5Util;
import com.nbst.comnutil.MyException;
import com.nbst.comnutil.NormalResult;
import com.nbst.comnutil.SpringContextUtil;
import com.nbst.comnutil.ToolUtils;
import com.nbst.dao.mapper.mes.BillMapper;
import com.nbst.dao.mapper.mes.OpenSystemMethodMapper;
import com.nbst.model.api.OpenMethod;
import com.nbst.model.api.OpenSystem;
import com.nbst.model.api.OpenSystemMethod;
import com.nbst.model.api.RequestModel;
import com.nbst.model.api.ResponseModel;
import com.nbst.service.api.OpenApiService;

import org.json.JSONException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import cn.hutool.json.JSONObject;
import lombok.extern.log4j.Log4j2;

/**
 * @author wangwt
 * @description 开放API服务实现类
 * @since 2020-10-13
 **/
@Log4j2
@Service
public class OpenApiServiceImpl implements OpenApiService {

    @Autowired
    OpenSystemMethodMapper openSystemMethodMapper;

    /**
     *  请求的时间与服务端的时间前后不能超过30分钟
     */
    private static final long TIMESTAMP_OUT = 30 * 60 * 1000L;

    /**
     * 请求成功
     */
    private static final String OK = "0000";

    /**
     * 失败
     */
    private static final String ERROR = "9999";

    /**
     * 令牌校验失败
     */
    private static final String SIGN_ERROR = "5555";

    /**
     * 无IP限制
     */
    private static final String ALL_IP = "*";

    /**
     * 配置IP的分割符
     */
    private static final String SEPARATOR = ",";

    @Autowired
	BillMapper billMapper;

    /**
     * 请求处理
     * @param requestModel
     * @return
     */
    @Override
    public ResponseModel sendOpenRequest(RequestModel requestModel, HttpServletRequest request) {
        //校验令牌信息
        ResponseModel responseModel = validateSign(requestModel, request);
        if(!responseModel.getCode().equals(OK)){
            return responseModel;
        }
        //反射出类名方法并调用
        return dealService(requestModel);
    }

    /**
     * 处理服务
     * @param requestModel
     * @return
     */
    private ResponseModel dealService(RequestModel requestModel){
        ResponseModel responseModel = new ResponseModel();
        responseModel.setCode(ERROR);
        responseModel.setMsg("操作失败");
        //查询方法
        OpenMethod openMethod = OpenMethodCache.getOpenMethod(requestModel.getMethod());
        if(openMethod == null){
            responseModel.setMsg("mothed已失效");
            return responseModel;
        }
        //全路径类名
        String classPath = openMethod.getClassPath();
        //调用方法名
        String serviceMethod = openMethod.getServiceMethod();
        //请求参数的全路径类型
        String requestClassPath = openMethod.getRequestClassPath();
        if(ToolUtils.isEmpty(classPath) || ToolUtils.isEmpty(serviceMethod)){
            responseModel.setMsg("mothed已失效");
            return responseModel;
        }
        //调用方法所在类
        Class<?> clazz = null;
        //调用方法对象
        Object obj = null;
        //调用的方法
        Method method = null;
        try {
            //获取调用方法所在类
            clazz = Class.forName(classPath);
            //获取调用方法对象
            obj = SpringContextUtil.getBean(clazz);
            if(ToolUtils.isEmpty(requestClassPath)) {
                method = clazz.getMethod(serviceMethod);
                //返回结果
                responseModel.setData(method.invoke(obj));
            } else {
                //请求的参数
                Object requestData = requestModel.getData();
                //入参的类
                Class<?> requestClazz = Class.forName(requestClassPath);
                //对象转换为相应的实体对象
                ObjectMapper objectMapper = new ObjectMapper();
                Object data = objectMapper.convertValue(requestData, requestClazz);
                //获取所在类的对象
                method = clazz.getMethod(serviceMethod, requestClazz);
                //返回结果
                responseModel.setData(method.invoke(obj, data));
            }
            responseModel.setCode(OK);
            responseModel.setMsg("令牌校验成功");
            responseModel.setMethod(requestModel.getMethod());
            responseModel.setTimestamp(System.currentTimeMillis());
        }catch (InvocationTargetException targetException){
            log.error(targetException);
            targetException.printStackTrace();
            //自定义异常
            Throwable throwable = targetException.getTargetException();
            if(throwable instanceof MyException){
                responseModel.setCode(((MyException) throwable).getCode());
                responseModel.setMsg(throwable.getMessage());
            }
        }catch (Exception e){
            log.error(e);
            e.printStackTrace();
        }
        return responseModel;
    }

    /**
     * 校验请求信息
     * @param requestModel
     * @return
     */
    private ResponseModel validateSign(RequestModel requestModel, HttpServletRequest request){
        ResponseModel responseModel = new ResponseModel();
        responseModel.setCode(SIGN_ERROR);
        responseModel.setMsg("令牌校验失败");
        if(requestModel==null){
            responseModel.setMsg("请求数据不能为空");
            return responseModel;
        }
        String systemCode =  requestModel.getSystemCode();
        String method =  requestModel.getMethod();
        Long timestamp =  requestModel.getTimestamp();
        String sign =  requestModel.getSign();

        //校验系统code
        if(ToolUtils.isEmpty(systemCode)){
            responseModel.setMsg("systemCode不能为空");
            return responseModel;
        }
        //方法不能为空
        if(ToolUtils.isEmpty(method)){
            responseModel.setMsg("method不能为空");
            return responseModel;
        }
        //令牌不能为空
        if(ToolUtils.isEmpty(sign)){
            responseModel.setMsg("sign不能为空");
            return responseModel;
        }
        //请求的时间戳
        if(timestamp == null){
            responseModel.setMsg("请求的时间戳不能为空");
            return responseModel;
        }
        //服务器时间
//        long now = System.currentTimeMillis();
        //请求时间要在服务器时间的30分钟内
//        if(now > timestamp + TIMESTAMP_OUT || now < timestamp - TIMESTAMP_OUT){
//            responseModel.setMsg("时间戳请传入当前时间");
//            return responseModel;
//        }
        //调用我方接口的系统配置信息,缓存中获取
        OpenSystem openSystem = OpenSystemCache.getOpenSystem(systemCode);
        if(openSystem == null){
            //没查到直接返回
            responseModel.setMsg("当前systemCode无权限调用");
            return responseModel;
        }
        //获取到当前的IP
        String currentIp = HttpUtil.getIpAddress(request);
        //校验IP黑白名单
        if(!checkOpenIp(openSystem.getOpenIp(), openSystem.getBlackIp(), currentIp)){
            responseModel.setMsg("非法IP");
            return responseModel;
        }
        Map map = new HashMap<>();
        map.put("systemCode", systemCode);
        map.put("method", method);
        OpenSystemMethod openSystemMethod = openSystemMethodMapper.queryOpenSystemMethod(map);
        if(openSystemMethod == null){
            responseModel.setMsg("method不存在或无权限调用");
            return responseModel;
        }
        responseModel.setMethod(method);
        String nowSign = MD5Util.encode(systemCode + openSystem.getSecret() + method + timestamp);
        if(sign.equals(nowSign)){
            responseModel.setCode(OK);
            responseModel.setMsg("令牌校验成功");
        }
        return responseModel;
    }

    /**
     * 校验IP信息
     * @param openIp
     * @return
     */
    private boolean checkOpenIp(String openIp, String blackIp, String currentIp){
        if(ALL_IP.equals(openIp) && ToolUtils.isEmpty(blackIp)){
            return true;
        }
        if(ALL_IP.equals(blackIp)){
            return false;
        }
        String[] ips = openIp.split(SEPARATOR);
        if(ips.length>0 && !ALL_IP.equals(openIp)){
            for (String ip : ips) {
                if (Pattern.matches(ip, currentIp)) {
                    return true;
                }
            }
        }
        //黑名单校验
        if(!ToolUtils.isEmpty(blackIp)) {
            String[] blackIps = blackIp.split(SEPARATOR);
            if (blackIps.length > 0) {
                for (String ip : blackIps) {
                    if (Pattern.matches(ip, currentIp)) {
                        return false;
                    }
                }
            }
        }
        return false;
    }

    /**
     * api联调测试
     * @param str
     * @return
     * @throws JSONException
     */
    public NormalResult wmsOrderReturn(JSONObject str) throws JSONException{
        NormalResult result = ToolUtils.getNormalResult();
        String orderNo = str.get("orderNbr")+"";
        String orderType = str.get("type")+"";
        Map map = new HashMap<>();
        map.put("fbillNo", orderNo);
        result.setRows(str);
        return result;
    }

}
